#include "SQLiteContentDAO.h"
#include <iostream>

SQLiteContentDAO::SQLiteContentDAO(sqlite3* database) {
	this->database = database;
}

void SQLiteContentDAO::save(const Content& content) {
	sqlite3_stmt* statement;
	const char* sql = "insert into content values (?,?,?,?,?)";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_null(statement, 1);
	sqlite3_bind_text(statement, 2, content.getName().c_str(), -1,
			SQLITE_STATIC);
	sqlite3_bind_int(statement, 3, content.getGenre().getId());
	sqlite3_bind_int(statement, 4, content.getBegin());
	sqlite3_bind_int(statement, 5, content.getDuration());

	rc = sqlite3_step(statement);
	sqlite3_finalize(statement);
	if (rc != SQLITE_DONE) {
		std::ostringstream oss;
		oss << "Cannot save the content. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}
}

void SQLiteContentDAO::update(const Content& content) {
	sqlite3_stmt* statement;
	const char* sql =
			"update content set name=?,duration=?,begin=?,end=? where id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_text(statement, 1, content.getName().c_str(), -1,
			SQLITE_STATIC);
	sqlite3_bind_int(statement, 2, content.getGenre().getId());
	sqlite3_bind_int(statement, 3, content.getBegin());
	sqlite3_bind_int(statement, 4, content.getDuration());
	sqlite3_bind_int(statement, 5, content.getId());

	rc = sqlite3_step(statement);
	sqlite3_finalize(statement);
	if (rc != SQLITE_DONE) {
		std::ostringstream oss;
		oss << "Cannot update the content. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}
}

void SQLiteContentDAO::remove(const Content& content) {
	sqlite3_stmt* statement;
	const char* sql = "delete from content where id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_int(statement, 1, content.getId());

	rc = sqlite3_step(statement);
	sqlite3_finalize(statement);
	if (rc != SQLITE_DONE) {
		std::ostringstream oss;
		oss << "Cannot remove the content. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}
}

Content SQLiteContentDAO::loadContentById(int id) {
	sqlite3_stmt* statement;
	const char* sql = "select * from content where id=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_int(statement, 1, id);

	rc = sqlite3_step(statement);
	if (rc != SQLITE_ROW) {
		if (rc == SQLITE_DONE)
			return Content();
		std::ostringstream oss;
		oss << "Cannot load the content. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	DAOFactory* daoFactory = DAOFactory::getDAOFactory();
	Genre genre = daoFactory->getGenreDAO()->loadGenreById(sqlite3_column_int(
			statement, 2));

	Content content(sqlite3_column_int(statement, 0),
			(const char*) sqlite3_column_text(statement, 1), genre,
			(time_t) sqlite3_column_int(statement, 2),
			(time_t) sqlite3_column_int(statement, 3));

	sqlite3_finalize(statement);

	return content;
}

Content SQLiteContentDAO::loadContentByName(const std::string& name) {
	sqlite3_stmt* statement;
	const char* sql = "select * from content where name=?";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	sqlite3_bind_text(statement, 1, name.c_str(), -1, SQLITE_STATIC);

	rc = sqlite3_step(statement);
	if (rc != SQLITE_ROW) {
		if (rc == SQLITE_DONE)
			return Content();
		std::ostringstream oss;
		oss << "Cannot load the content. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	DAOFactory* daoFactory = DAOFactory::getDAOFactory();
	Genre genre = daoFactory->getGenreDAO()->loadGenreById(sqlite3_column_int(
			statement, 2));

	Content content(sqlite3_column_int(statement, 0),
			(const char*) sqlite3_column_text(statement, 1), genre,
			(time_t) sqlite3_column_int(statement, 2),
			(time_t) sqlite3_column_int(statement, 3));

	sqlite3_finalize(statement);

	return content;
}

std::list<Content> SQLiteContentDAO::loadAll() {
	sqlite3_stmt* statement;
	const char* sql = "select * from content";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	std::list<Content> contents;
	DAOFactory* daoFactory = DAOFactory::getDAOFactory();
	int cid;
	Genre genre;

	while (true) {
		rc = sqlite3_step(statement);
		if (rc != SQLITE_ROW) {
			if (rc == SQLITE_DONE)
				break;
			std::ostringstream oss;
			oss << "Cannot load the contents. SQLite code: " << rc;
			throw SQLiteException(oss.str());
		}

		cid = sqlite3_column_int(statement, 0);
		genre = daoFactory->getGenreDAO()->loadGenreById(sqlite3_column_int(
				statement, 2));

		contents.push_back(Content(cid, (const char*) sqlite3_column_text(
				statement, 1), genre, sqlite3_column_int(statement, 3),
				sqlite3_column_int(statement, 4)));
	}

	sqlite3_finalize(statement);

	return contents;
}

int SQLiteContentDAO::countAll() {
	sqlite3_stmt* statement;
	const char* sql = "select count(id) from content";

	int rc = sqlite3_prepare_v2(database, sql, -1, &statement, NULL);
	if (rc != SQLITE_OK) {
		std::ostringstream oss;
		oss << "Error on prepare statement. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	rc = sqlite3_step(statement);
	if (rc != SQLITE_ROW) {
		std::ostringstream oss;
		oss << "Cannot load content. SQLite code: " << rc;
		throw SQLiteException(oss.str());
	}

	int count = sqlite3_column_int(statement, 0);
	sqlite3_finalize(statement);

	return count;
}
